package com.gitee.chenyaohua;

import com.gitee.chenyaohua.conn.ConnectionPoolFactory;
import com.gitee.chenyaohua.conn.DbUtils;
import com.gitee.chenyaohua.dto.AttrValue;
import com.gitee.chenyaohua.dto.Generation;
import com.gitee.chenyaohua.dto.Mapper;
import com.gitee.chenyaohua.dto.Table;
import com.gitee.chenyaohua.utils.ColumnType;
import com.gitee.chenyaohua.utils.DBType;
import com.gitee.chenyaohua.utils.FreemarkerUtil;
import com.gitee.chenyaohua.utils.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import javax.annotation.Resource;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.sql.Connection;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Mojo(name = "generation",defaultPhase = LifecyclePhase.COMPILE)
public class GenerationCode extends AbstractMojo {

    /**
     * maven仓库目录
     */
    @Parameter( defaultValue = "${localRepository}", readonly = true, required = true )
    protected ArtifactRepository localRepository;
    /**
     * 项目根目录
     */
    @Parameter(defaultValue = "${basedir}")
    private File baseDir;
    /**
     * 资源目录
     */
    @Parameter(defaultValue = "${project.build.resources}",readonly = true,required = true)
    private List<Resource> resources;
    /**
     * 源码目录
     */
    @Parameter(defaultValue = "${project.build.sourceDirectory}",required = true,readonly = true)
    private File sourceDir;
    /**
     * 测试资源
     */
    @Parameter(defaultValue = "${project.build.testResources}",readonly = true,required = true)
    private List<Resource> testResources;
    /**
     * 测试代码目录
     */
    @Parameter(defaultValue = "${project.build.testSourceDirectory}",readonly = true,required = true)
    private File testSourceDir;
    /**
     * 驱动类名
     */
    @Parameter(property ="generationConfigPath")
    private String generationConfigPath;

    public static ThreadLocal<GenericObjectPool<Connection> > CONNECTION_PARAMS = ThreadLocal.withInitial(()->null);

    public static class  Column{
        public static final String JDBC_TYPE = "jdbcType";

        public static final String JAVA_TYPE = "javaType";

        public static final String IS_NULL = "isNull";

        public static String COLUMN_NAME="columnName";

        public static String ATTR_NAME="attrName";

        public static String TABLE_NAME="tableName";

    }

    /**
     * 初始化参数
     * @param username
     * @param jdbcUrl
     * @param driverClass
     * @param password
     */
    private static void init(String username,String jdbcUrl,String driverClass,String password){
        if(CONNECTION_PARAMS.get() == null){
            ConnectionPoolFactory factory = new ConnectionPoolFactory(driverClass, username,jdbcUrl,password);
            GenericObjectPool<Connection> pool = new GenericObjectPool<>(factory);
            CONNECTION_PARAMS.set(pool);
        }
    }

    @Override
    public void execute() {
        //初始化
        try {
            Generation generation = resolveDOM();
            List<com.gitee.chenyaohua.dto.Connection> connection = generation.getConnection();

            for (com.gitee.chenyaohua.dto.Connection connection1 : connection) {
                init(connection1.getUsername(),connection1.getJdbcUrl(),connection1.getDriverClass(),connection1.getPassword());
                DbUtils dbUtils = new DbUtils( CONNECTION_PARAMS.get());

                List<Table> table = connection1.getTable();
                for (Table tb : table) {
                    HashMap<String,Object> map = new HashMap<>();

                    if(StringUtils.isBlank(tb.getTableName())){
                        throw new RuntimeException("表名不能为空");
                    }

                    String dbType = dbUtils.queryDBType();
                    DBType type = DBType.pase(dbType);
                    String sql;
                    List<Map<String,Object>> tableInfo;
                    String tableComment;
                    List<Map<String,Object>> columnKeys = new ArrayList<>();
                    switch (type){
                        case MYSQL:
                            sql = "select COLUMN_KEY as columnKey,TABLE_NAME as tableName,TABLE_SCHEMA as tableSchema," +
                                    "COLUMN_COMMENT as columnComment,COLUMN_NAME as columnName,COLUMN_DEFAULT as columnDefault," +
                                    "DATA_TYPE as jdbcType,CHARACTER_MAXIMUM_LENGTH as maxLength,IS_NULLABLE as isNull " +
                                    "from information_schema.COLUMNS where TABLE_NAME=? and TABLE_SCHEMA = database()";
                            tableInfo = dbUtils.select(sql, tb.getTableName());

                            sql = "SELECT TABLE_COMMENT as tableComment FROM Information_schema.TABLES WHERE TABLE_NAME=? and TABLE_SCHEMA = database()";
                            List<Map<String,Object>> tabs = dbUtils.select(sql, tb.getTableName());

                            tableComment = (String) tabs.get(0).get("tableComment");
                            if(tableInfo == null || tableInfo.isEmpty()){
                                throw new RuntimeException("不存在该表的相关列信息");
                            }
                            for (Map<String, Object> columns : tableInfo) {

                                String columnName = (String) columns.get(Column.COLUMN_NAME);
                                columnName = StringUtils.strCamelCase(columnName);
                                columns.put(Column.ATTR_NAME,columnName);

                                String jdbcType = (String) columns.get(Column.JDBC_TYPE);
                                columns.put(Column.JAVA_TYPE, ColumnType.parse(jdbcType).getJavaType());
                                String columnKey = (String) columns.get("columnKey");
                                if(!StringUtils.isBlank(columnKey)){
                                    columnKeys.add(columns);
                                }

                            }


                            break;
                        default:
                            throw new RuntimeException("该数据库类型暂不知支持");
                    }

                    map.put("tableName",tb.getTableName());
                    map.put("tableAlias",tb.getAlias());
                    map.put("columns",tableInfo);
                    map.put("tableComment",tableComment);
                    map.put("keys",columnKeys);

                    List<Mapper> mapper = tb.getMapper();
                    for (Mapper mp : mapper) {
                        String outPath = mp.getOutPath();
                        String tmpPath = mp.getTmpPath();

                        outPath=getPath(outPath,generation.getProperties());
                        tmpPath=getPath(tmpPath,generation.getProperties());

                        FreemarkerUtil.generateJavaFile(tmpPath, outPath,map);
                    }
                }


            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String getPath(String outPath, List<AttrValue> properties) {
        Pattern compile = Pattern.compile("(\\$\\{)([\\w]+)(\\})");
        Matcher matcher = compile.matcher(outPath);

        StringBuffer sf = new StringBuffer();

        while (matcher.find()) {

            String group = matcher.group().replaceAll("[(\\$\\{)|(\\})]","").trim();

            Optional<AttrValue> first = properties.stream().filter(x -> x.getKey().trim().equals(group)).findFirst();

            if (first.isPresent()) {
                String value = first.get().getValue();
                matcher.appendReplacement(sf,value);

            }

        }

        matcher.appendTail(sf);
        return sf.toString();
    }


    public Generation resolveDOM() throws JAXBException {
        File file = new File(generationConfigPath);
        JAXBContext jaxbContext = JAXBContext.newInstance(Generation.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        return (Generation)unmarshaller.unmarshal(file);

    }

}
